/*
 *  RS232Receiver.java
 *  
 *  copyright (c) 2006, tom dinchak & lorenzo orselli
 * 
 *  this file is part of jserialio.
 *
 *  jserialio is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  jserialio is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  You should have received a copy of the GNU General Public License
 *  along with jserialio; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * 
 *  created on July 29, 2006, 1:55 AM
 *
 */

package serialio;

import java.io.*;

import com.illposed.osc.*;
import java.net.*;
import java.util.Enumeration;

import gnu.io.*;

/**
 * 
 * @author tom
 */
public class RS232Receiver extends Thread implements
gnu.io.SerialPortEventListener {

	OSCPortOut outport = null;

	InputStream serialin = null;

	OutputStream serialout = null;

	boolean threadStop = false;
	boolean unplugged = false;

	String OSCOutPortStr = null;

	CommPort commport = null;
	CommPortIdentifier commportidentifier = null;
	SerialPort serialport = null;
	CommPortOwnershipListener portListener = null;

	String identifier = null;
	String baudrate = null;

	SerialIOProcess parent;

	/** Creates a new instance of RS232Receiver */

	public String getStatus() {
		if (threadStop == true) {
			return "Stopped";
		} else {
			return "Started";
		}
	}

	public void stopReceiver() {
		if (serialport != null) {
			serialport.removeEventListener();
			serialport.close();
		}
		if (commport != null) {
			commport.close();
		}
		this.threadStop = true;
	}

	public RS232Receiver(String newoscoutportstr, SerialIOProcess parent, CommPortOwnershipListener portListener) {
		this.parent = parent;
		this.OSCOutPortStr = newoscoutportstr;
		this.portListener = portListener;
	}

	public void sendPress(int val1, int val2, int val3) {
		OSCMessage msg = new OSCMessage();
		msg.setAddress("/box/press");
		msg.addArgument(new Integer(val1));
		msg.addArgument(new Integer(val2));
		msg.addArgument(new Integer(val3));
		try {
			outport.send(msg);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void sendAdcVal(int port, float value) {
		OSCMessage msg = new OSCMessage();
		msg.setAddress("/box/adc");
		msg.addArgument(new Integer(port));
		msg.addArgument(new Float(value));
		try {
			outport.send(msg);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		System.out.println("Starting up RS232Receiver...");
		try {
			outport = new OSCPortOut(InetAddress.getByName("localhost"),
					Integer.parseInt(OSCOutPortStr));
			SerialPort serialport = (SerialPort) commport;
			serialport.addEventListener(this);
			serialport.notifyOnDataAvailable(true);

			while (threadStop == false) {
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
				}
			}
			System.out.println("RS232Receiver stopped.");
			try {
				parent.oscr.stopReceiver();
			} catch (SocketException e) {
				//
			}
			serialport.removeEventListener();
			serialport.close();
			commport.close();
			throw new Exception("Thread ended");
		} catch (Exception e) {
			parent.RestartRS232Receiver();
			e.printStackTrace();
		}

	}

	public void serialEvent(SerialPortEvent event) {
		try {

			switch (event.getEventType()) {

			case SerialPortEvent.BI:
				System.out.println("BI received");
				threadStop = true;
			case SerialPortEvent.OE:
				System.out.println("OE received");
				threadStop = true;
			case SerialPortEvent.FE:
				System.out.println("FE received");
				threadStop = true;
			case SerialPortEvent.PE:
				System.out.println("PE received");
				threadStop = true;
			case SerialPortEvent.CD:
				System.out.println("CD received");
				threadStop = true;
			case SerialPortEvent.CTS:
				System.out.println("CTS received");
				threadStop = true;
			case SerialPortEvent.DSR:
				System.out.println("DSR received");
				threadStop = true;
			case SerialPortEvent.RI:
				System.out.println("RI received");
				threadStop = true;
			case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
				System.out.println("OUTPUT BUFFER EMPTY received");
				threadStop = true;
				break;
			case SerialPortEvent.DATA_AVAILABLE:
				byte[] readbuffer = new byte[128];
				int numBytes = 0;
				while (serialin.available() > 0) {
					numBytes = serialin.read(readbuffer);
				}
				int i = 0;
				while (numBytes > 0) {
					doEvent(readbuffer[i], readbuffer[i + 1]);
					numBytes-=2;
					i += 2;
				}
				break;
			}
		} catch (IOException e) {
			try {
				parent.oscr.stopReceiver();
			} catch (java.net.SocketException s) {
				//
			}
			System.out.println("Monome disconnected -- shutting down.");
			System.exit(0);
			checkPorts();			

		}

	}

	public void checkPorts() {
		while (true) {
			Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();
			while ( portEnum.hasMoreElements() ) 
			{
				CommPortIdentifier portIdentifier = (CommPortIdentifier) portEnum.nextElement();
				if (portIdentifier.getName().equals(identifier) && parent.senderror != true) {
					System.out.println("Monome reconnected after disconnect -- attempting to recover.");
					threadStop = true;
					parent.RestartRS232Receiver();
					return;
				}                
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {

			}
		}
	}

	public SerialPortResponse setSerialPort(String identifier, String baudrate) {
		this.identifier = identifier;
		this.baudrate = baudrate;
		try {
			commportidentifier = CommPortIdentifier.getPortIdentifier(identifier);
			commportidentifier.addPortOwnershipListener(portListener);

			if (commportidentifier.isCurrentlyOwned()) {
				System.out.println("Error: "+identifier+" currently in use.");
				return null;
			}
			commport = commportidentifier.open("SerialIO", 2000);

		} catch (NoSuchPortException e) {
			System.out.println("Error: "+identifier+ ": unknown port");
			return null;
		} catch (PortInUseException e) {
			System.out.println("Error: "+identifier+ ": port in use");
			return null;
		}

		if (commport instanceof SerialPort) {
			Integer BaudRateInt = new Integer(0);
			BaudRateInt = Integer.parseInt(baudrate);

			try {
				serialport = (SerialPort) commport;
				serialport.setSerialPortParams(BaudRateInt.intValue(),
						SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
						SerialPort.PARITY_NONE);
				serialin = serialport.getInputStream();
				serialout = serialport.getOutputStream();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return new SerialPortResponse(serialin, serialout);
	}

	private void doEvent(byte msb, byte lsb) {
		try {
			// press event
			if (msb >> 4 == 0) {
				int x = lsb >> 4;
				int y = lsb & 15;
				MonomeOrientation mo = new MonomeOrientation();
				int xy[] = mo.getMonomeCoord(SerialIOProcess.CableOrientation, x, y);
				x = xy[0];
				y = xy[1];
				int d = msb;
				sendPress(x, y, d);
				System.out.println("[press event] x: " + x + " y:" + y + " d: "
						+ msb);
				// adc event
			} else if (msb >> 4 == 1) {
				int port = (msb >> 2) - 4;
				float value = lsb;
				sendAdcVal(port, value);
				System.out.println("[adc event] port: " + port + " value: "
						+ value);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}
